package Implementacion;

import Interfaces.Ficha;
import Interfaces.IJuego;
import TDA.MatrizTDA;
import TDA.VectorTDA;
import TDA.ConjuntoTDA;
import Implementaciones.Matriz;
import Implementaciones.Vector;
import Implementaciones.Conjunto;


public class Juego implements IJuego {

	/*
	 * Cada casilla del tablero contendr� el nombre de la ficha que lo ocup�.
	 * En caso de que el tablero no tenga ficha, tendr� el valor null por defecto, o un valor que se quiera setear como casilla no usada
	 */
	
	static int altoTablero = 6;		//POSIBLES: 6x10, 5x12, 4x15, 3x20
	static int anchoTablero = 10;
	
	private static class Posicion{
		int i;
		int j;
	}
	
	String [][]Tablero = new String[altoTablero][anchoTablero];
	
	
	public boolean Rompecabeza2D(MatrizTDA<Ficha> fichas) throws Exception{
		//VectorTDA<Boolean> SolParcial = new Vector<Boolean>();
		//SolParcial.inicializarVector(anchoTablero);
		//String[] fichasUsadasArray = new String[12];
		VectorTDA<Ficha> fichasUsadasArray = new Vector<Ficha>();
		fichasUsadasArray.inicializarVector(12);
		ConjuntoTDA<Ficha> fichasUsadas = new Conjunto<Ficha>();
		fichasUsadas.inicializarConjunto();
		int etapa = 0;
			
		return backPentomino (Tablero, fichas, fichasUsadasArray,etapa);	
	}
	
	private Boolean RecuperarValor(VectorTDA<Ficha> fichaArray, int posicion, String nombre){
		try{
			for(int i = 0; i <= posicion; i++){
				//System.out.println("Recuperar Valor: "+(fichaArray.recuperarElemento(i)));
				Ficha fichaRecuperada = fichaArray.recuperarElemento(i);
				//System.out.println("Ficha recuperada: "+fichaRecuperada.obtenerNombre()+"<=> nombre: "+nombre);
				if(fichaRecuperada.obtenerNombre() == nombre){
					//System.out.println("true");
					return true;
				}
			}
			//return (fichaArray.recuperarElemento(posicion) != null);
		}catch(Exception e){
			//System.out.println("Catch<FALSE>"+e.getMessage());
			return false;
		}
		//System.out.println("--FALSE--");
		return false;
	}
	/*
	//VALIDA SI UNA PIEZA PUEDE SER INSERTADA EN LA PROXIMA POSICION LIBRE DE LA COLUMNA DEL TABLERO
	private boolean piezaValida(String [][] Tablero, Ficha f){
		Posicion pos = new Posicion();
		pos = buscarPrimeraPosicionLibre(Tablero);
		
		
		return true;
	}
	
	//INSERTA UNA PIEZA EN EL TABLERO
	private void insertarPieza(String [][] Tablero, Ficha f){
		Posicion pos = new Posicion();
		pos = buscarPrimeraPosicionLibre(Tablero);
		
		
	}
	*/
	//RETORNA LAS COORDENADAS (i,j) DE LA PRIMERA POSICION LIBRE EN EL TABLERO (BUSCA POR COLUMNA ASCENDENTEMENTE)
	private Posicion buscarPrimeraPosicionLibre(String [][] Tablero){
		Posicion pos = new Posicion();
		for(int j=0; j<anchoTablero; j++){
			for(int i=0; i<altoTablero ; i++){
				if(Tablero[i][j] == null){
					pos.i= i;
					pos.j= j;
					return pos;
				}
			}
		}
		return null;
	}
	
	//CUANDO UNA COLUMNA DEL TABLERO ESTA COMPLETA RETORNA TRUE
	private boolean etapaCompleta(String [][] Tablero, int etapa){
		int cantFilas= 0;
		for(int i=0; i < altoTablero; i++){
			if(Tablero[i][etapa] != null){
				cantFilas++;
			}
		}
		//System.out.println("Cantfilas: "+cantFilas+" AnchoTablero: "+anchoTablero);
		return (cantFilas == altoTablero);
		/*if(cantFilas == altoTablero){
			return true;
		}
		else{
			return false;
		}*/
	}
	
	//BACKTRACKING DEL PENTOMINO
	public boolean backPentomino(String[][] Tablero, MatrizTDA<Ficha> fichas, VectorTDA<Ficha> fichasUsadasArray,int etapa) throws Exception {
		System.out.println("-------BACKTRCING------");
		boolean solucion = false;
		int n = anchoTablero;
		int cantFichas = fichas.obtenerDimension();
		int valorDelValidar;
		int aux = 0;
		//System.out.println("Etapa: "+etapa);
		//System.out.println("Cantidad fichas: "+cantFichas);
		while (etapa < n && !solucion) {
			for (int i = etapa; i < cantFichas; i++) {
				int j = 0;
				//System.out.println("J: "+j);
				boolean proximaFicha = false;
				while (j < cantFichas && !proximaFicha && (fichas.obtenerValor(i, j) != null)) {
					Ficha ficha = fichas.obtenerValor(i, j);
					//System.out.println("SACO DE LA MATRIZ LA FICHA DE LA POSICION: ("+ i + "," + j + ")");
					//System.out.println("SACO DE LA MATRIZ LA FICHA: "+ficha.obtenerNombre());
					//System.out.println("j: "+j);SimuladorInsertarFicha(Tablero, ficha)
					valorDelValidar = ValidarPosicionAInsertarFicha(ficha);
					if (valorDelValidar != 0 && (!RecuperarValor(fichasUsadasArray, etapa, ficha.obtenerNombre()))){ //&& !etapaCompleta(Tablero, etapa)) {
						//InsertarFicha(Tablero, ficha);
						//System.out.println("j: "+j);
						//System.out.println("Etapa: "+etapa);
						InsertarFichaSegunCaso(ficha,valorDelValidar);
						fichasUsadasArray.agregarElemento(aux, ficha);
						aux++;
						proximaFicha = true;
						//System.out.println("IMPRIMIR");
						ImprimirTablero();
					}
					j++;
														
				}
				//System.out.println("No entre al if, la estapaCompleta da "+etapaCompleta(Tablero, etapa));
				if(etapaCompleta(Tablero, etapa)){
					if (etapa == n){ 
						return true;
					}else{ 
						//System.out.println("Back");
						backPentomino(Tablero, fichas, fichasUsadasArray,++etapa);
					}
				}
			}
		}
		return solucion;
	}
	
	
	/**
	 * Se deber� mostrar por pantalla c�mo qued� cubierto el tablero, de acuerdo a c�mo fueron las piezas organizadas.
	 * En cada casilla del tablero deber� ir la identificaci�n de la ficha "Nombre", que cubri� la misma.
	 */
	
	private void ImprimirTablero(){
		for(int f = 0; f < Tablero.length; f++) {
			for(int c = 0; c < Tablero[0].length; c++) {
				if(Tablero[f][c] == null) {
					System.out.print("- ");
				} else {
					System.out.print(Tablero[f][c] + " ");
				}
			}
			System.out.print("\n");
		}
		System.out.println("");
		System.out.println("");
	}
	private void ImprimirFicha(Ficha ficha){
		for(int f = 0; f < ficha.obtenerF(); f++) {
			for(int c = 0; c < ficha.obtenerC(); c++) {
				if(ficha.valor(f, c) == false) {
					System.out.print("- ");
				} else {
					System.out.print("# ");
				}
			}
			System.out.print("\n");
		}
		System.out.println("");
		System.out.println("");
	}
	//HACE LA SIMULACION DE LA INSERCION DE LA FICHA EN EL TABLERO
	//DEVUELVE "TRUE" SI ES POSIBLE INSERTARLA, Y "FALSE" EN CASO CONTRARIO
	public boolean SimuladorInsertarFicha(String[][] Tablero, Ficha ficha) {
		//System.out.println("#################### SIMULACION ####################");
		Posicion pos = buscarPrimeraPosicionLibre(Tablero);
		//System.out.println("PRIMERA POSICION LIBRE: (" +pos.i + "," + pos.j + ")");
		//System.out.println("nombre ficha: "+ficha.obtenerNombre());
		int compararHastaFila = ficha.obtenerF();//(pos.i + ficha.obtenerF());
		int compararHastaColumna = (pos.j + ficha.obtenerC());
		int fichaFila = 0;
		int fichaColumna = 0;
		//if(ficha.obtenerNombre().equalsIgnoreCase("F")){
			//ImprimirFicha(ficha);
			//System.out.println("PRIMERA POSICION LIBRE: (" +pos.i + "," + pos.j + ")");
		//}
		//int compararDesdeFila = Math.max((pos.i - getAlturaFicha(ficha)),0);
		//System.out.println("CompararDesdeFila: "+compararDesdeFila);
		//if (compararDesdeFila < 0) {
			//System.out.println("COMPARAR DESDE FILA ES MENOR A 0");
			//System.out.println("FALSE");
		//	return false;
		//} 
		//else {
			try {
				//System.out.println("tableroFila: "+Tablero.length+" Columna: "+Tablero[0].length);
				//System.out.println("SIMLULO DESDE FILA: " +compararDesdeFila);
				//System.out.println("SIMLULO HASTA FILA: " +compararHastaFila);
				//System.out.println("SIMLULO HASTA COLUMNA: " +compararHastaColumna);
				for (int c = pos.j; (fichaColumna < compararHastaColumna); c++){
					fichaFila = 0;
					//System.out.println("Valor de c: "+c);
					//	System.out.println("Valor de fichaColumna: "+fichaColumna);
					if(c == Tablero[0].length)
						return false;
					for (int f = pos.i; (fichaFila < compararHastaFila); f++){
						//System.out.println("Valor de f: "+f);
				//		System.out.println("Valor de fichaFila: "+fichaFila);
						//System.out.println("Valor de En tabla: "+Tablero[f][c]);
						if(f == Tablero.length)
							return false;
					//	System.out.println("Valor de de la ficha "+ ficha.valor(f - compararDesdeFila , c - pos.j));
						if (Tablero[f][c] != null && ficha.valor(fichaFila, fichaColumna)){
							//System.out.println("FALSE");
							return false;
						}
						fichaFila++;
					}
					fichaColumna++;
				}
			} catch (IndexOutOfBoundsException e) {
				//System.out.println("Catch<False>");
				return false;
			}
			//System.out.println("Simulador=>true");
			return true;
		//}
	}

	//DEVUELVE LA CANTIDAD DE ESPACIOS EN BLANCO QUE TIENE LA FICHA A INSERTAR (EN LA COLMUNA "0")
	//HASTA EL PRIMER ESPACIO QUE NO SEA BLANCO. EL OBJETIVO ES PODER ENCASTRAR LA FICHA EN EL TABLERO
	public int getAlturaFicha(Ficha ficha) {
		int fila = 0;
		while(fila <= ficha.obtenerF() && !ficha.valor(fila, 0)) {
			fila++;
		}
		//System.out.println("ALTURA DE LA FICHA: " +fila);
		return fila;
	}
	
	//INSERTA LA FICHA EN EL TABLERO SI LA SIMULACION DIO RESULTADO "OK"
	public void InsertarFicha(String[][] Tablero, Ficha ficha) {
		//System.out.println("#################### INSERTAR ####################");
		Posicion pos = buscarPrimeraPosicionLibre(Tablero);
		//System.out.println("VOY A TRATAR DE INSERTAR LA FICHA: " + ficha.obtenerNombre());
		//System.out.println("EN LAS COORDENADAS: (" +pos.i + "," + pos.j + ")");
		int insertarDesdeFila = Math.max((pos.i - getAlturaFicha(ficha)),0);
		int insertarHastaFila = (pos.i + ficha.obtenerF());
		int insertarHastaColumna = (pos.j + ficha.obtenerC());
		//System.out.println("INSERTAR DESDE FILA: " +insertarDesdeFila);
		//System.out.println("INSERTAR HASTA FILA: " +insertarHastaFila);
		//System.out.println("INSERTAR HASTA COLUMNA: " +insertarHastaColumna);
		try {
			//ImprimirFicha(ficha);
			for (int c = pos.j; c < insertarHastaColumna; c++){
				for (int f = insertarDesdeFila; f < insertarHastaFila; f++){
					if(ficha.valor(f-insertarDesdeFila, c-pos.j)){
						Tablero[f][c] = null;
						Tablero[f][c] = ficha.obtenerNombre();
					}
				}
			}
						
		}
		catch (IndexOutOfBoundsException e) {
			System.out.printf("###Method: InsertarFicha\nSe trato de acceder a una posicion invalida###\n");
			
		}
	}
	private void InsertarFichaSegunCaso(Ficha ficha, int caso){
		Posicion pos = buscarPrimeraPosicionLibre(Tablero);
		int valorEntreNulls = ObtenerPosicionFilaEntreNull(ficha);
		ImprimirFicha(ficha);
		switch(caso){
		case 1: if(valorEntreNulls == -1)
					InsertarArribaAbajo(ficha, pos.i,pos.j);
				else
					InsertarArribaAbajo(ficha, pos.i,pos.j, valorEntreNulls);
			break;
		case 2: if(valorEntreNulls == -1)
					InsertarAbajoArriba(ficha,pos.i,pos.j);
				else
					InsertarAbajoArriba(ficha,pos.i,pos.j, valorEntreNulls);
			break;
		}
	}
	private void InsertarAbajoArriba(Ficha ficha, int posF, int posC, int posFValorEntreNull) {
		System.out.println("ABAJOARRIBA- override");
		int auxFila = posF;
		int auxColumna = posC;
		boolean first = true;
		for(int c = 0; c < ficha.obtenerC(); c++){
			auxFila = posF;
			for(int f = (ficha.obtenerF()-1); f >= 0; f--){
				if(first){
					if(ficha.valor(posFValorEntreNull, c))
						Tablero[auxFila][auxColumna] = ficha.obtenerNombre();
					first = false;
				}else{
					if(ficha.valor(f, c))
						Tablero[auxFila][auxColumna] = ficha.obtenerNombre();
				}
				auxFila--;
			}
			auxColumna++;
		}
	}
	private void InsertarAbajoArriba(Ficha ficha, int posF, int posC) {
		System.out.println("ABAJOARRIBA");
		int auxFila = posF;
		int auxColumna = posC;
		for(int c = 0; c < ficha.obtenerC(); c++){
			auxFila = posF;
			for(int f = (ficha.obtenerF()-1); f >= 0; f--){
				//System.out.println("F: "+f);
				//System.out.println("auxFila: "+auxFila);
				//System.out.println("auxColumna: "+auxColumna);
				if(ficha.valor(f, c))
					Tablero[auxFila][auxColumna] = ficha.obtenerNombre();
				auxFila--;
			}
			auxColumna++;
		}
	}

	private void InsertarArribaAbajo(Ficha ficha, int posF, int posC, int posFValorEntreNull) {
		System.out.println("ArribaAbajo - override");
		int auxFila = posF;
		int auxColumna = posC;
		boolean first = true;
		for(int c = 0; c < ficha.obtenerC(); c++){
			auxFila = posF;
			for(int f = 0; f < ficha.obtenerF(); f++){
				if(first){
					if(ficha.valor(posFValorEntreNull, c))
						Tablero[auxFila][auxColumna] = ficha.obtenerNombre();
					first = false;
				}else{
					if(ficha.valor(f, c))
						Tablero[auxFila][auxColumna] = ficha.obtenerNombre();
				}
				auxFila++;				
			}
			auxColumna++;
		}
			
	}
	private void InsertarArribaAbajo(Ficha ficha, int posF, int posC) {
		System.out.println("ArribaAbajo");
		int auxFila = posF;
		int auxColumna = posC;
		for(int c = 0; c < ficha.obtenerC(); c++){
			auxFila = posF;
			for(int f = 0; f < ficha.obtenerF(); f++){
				if(ficha.valor(f, c))
					Tablero[auxFila][auxColumna] = ficha.obtenerNombre(); 
				auxFila++;				
			}
			auxColumna++;
		}
			
	}
	private int ValidarPosicionAInsertarFicha(Ficha ficha){
		int valorCaso = 0;
		Posicion pos = buscarPrimeraPosicionLibre(Tablero);
		switch(pos.i){
		case 0: valorCaso = ObtenerFichaArribaAbajo(pos.i, pos.j, ficha);
			break;
		case 5: valorCaso = ObtenerFichaAbajoArriba(pos.i, pos.j, ficha);
			break;
		default: valorCaso = ObtenerFichaArribaAbajo(pos.i, pos.j, ficha);
				if(valorCaso == 0)
					valorCaso = ObtenerFichaArribaAbajo(pos.i, pos.j, ficha);
			break;
		}
		return valorCaso;
	}
	private int ObtenerFichaArribaAbajo(int posActualFila, int posActualColumna,Ficha ficha){
		int auxFila = posActualFila;
		int auxColumna = posActualColumna;
		for(int c = 0; c < ficha.obtenerC(); c++){
			auxFila = posActualFila;
			for(int f = 0; f < ficha.obtenerF(); f++){
				if(Tablero[auxFila][auxColumna] != null && ficha.valor(f, c))
					return 0;
				auxFila++;
			}
			auxColumna++;
		}
		return 1;
	}
	
	private int ObtenerFichaAbajoArriba(int posActualFila, int posActualColumna,Ficha ficha){
		int auxFila = posActualFila;
		int auxColumna = posActualColumna;
		for(int c = 0; c < ficha.obtenerC(); c++){
			auxFila = posActualFila;
			for(int f = (ficha.obtenerF()-1); f >= 0; f--){
				if(Tablero[auxFila][auxColumna] != null && ficha.valor(f, c))
					return 0;
				auxFila--;
			}
			auxColumna++;
		}
		return 2;
	}
	
	
	private int ObtenerPosicionFilaEntreNull(Ficha ficha)
	{
		int posFila = -1;
		if(!ficha.valor(0, 0) && !ficha.valor((ficha.obtenerF()-1), 0)){
			for(int i = 0; i < ficha.obtenerF(); i++){
				if(ficha.valor(i, 0))
					posFila = i;
			}
		}
		return posFila;
	}
}
